//****************************************************************************
//****************************************************************************
// Copyright(c) 2009-2010 Intel Corporation
//
// DISTRIBUTABLE AS SAMPLE SOURCE SOFTWARE
//
// This Distributable as Sample Source Software is subject to the terms
// and conditions of the Intel Software License Agreement for the Intel(R)
// Media Processor Software Development
//****************************************************************************
//****************************************************************************

// simple MP3 player - HDMI support (including hot plug) and supports running
// while other players run.

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

#include "osal.h"
#include "ismd_core.h"
#include "ismd_global_defs.h"
#include "ismd_audio_defs.h"
#include "ismd_audio.h"
#include "gen3_audio.h"
#include "file_source.h"
#include "pal_soc_info.h"
#include "libgdl.h"

#define xstringify(s) stringify(s)
#define stringify(s) #s
#ifdef COMP_VER
char *mp_version_string="#@# mp " xstringify(COMP_VER) " " __DATE__ " " __TIME__;
#else
char *mp_version_string="#@# mp 0.0.0.0000 <- COMP_VER undefined> " __DATE__ " " __TIME__;
#endif


static ismd_audio_processor_t       global_processor_h;
static ismd_dev_t                   input_h;
static ismd_port_handle_t           port_h;
static ismd_event_t                 eos_event_h;
static ismd_audio_output_t          output_handle_i2s0, output_handle_i2s1, output_handle_spdif;
static ismd_audio_output_t          output_handle_hdmi = AUDIO_INVALID_HANDLE;
static ismd_audio_output_config_t   audio_out_config = {0,16,ISMD_AUDIO_STEREO,ISMD_AUDIO_OUTPUT_PCM,48000,0};
static pl_filesource_t              filesource;

/*use different way to write data*/
char buf[1024];


static bool enable_edid(void) {
   bool                 success     = false;
   gdl_ret_t            gdl_result  = GDL_SUCCESS;
   gdl_pd_attribute_t   edid_enabled;
   int                  read_edid   = 1;
   gdl_display_info_t   display_info;

   gdl_result = gdl_port_get_attr(GDL_PD_ID_HDMI, GDL_PD_ATTR_ID_USE_EDID, &edid_enabled);
   if (gdl_result == GDL_SUCCESS) {
      if (edid_enabled.content._bool.value != GDL_TRUE) {
         gdl_result = gdl_port_set_attr(GDL_PD_ID_HDMI, GDL_PD_ATTR_ID_USE_EDID, &read_edid);
      }
      if (gdl_result == GDL_SUCCESS) {
         success = true;
      }
   }

   // HDMI audio won't work unless there is some display mode set
   // Check for a valid display mode, set one up if there is none
   gdl_result = gdl_get_display_info(GDL_DISPLAY_ID_0, &display_info);
   if (gdl_result == GDL_ERR_TVMODE_UNDEFINED) {

      display_info.id = GDL_DISPLAY_ID_0;
      display_info.tvmode.width = 1280;
      display_info.tvmode.height = 720;
      display_info.tvmode.refresh    = GDL_REFRESH_59_94;
      display_info.tvmode.interlaced = GDL_FALSE;
      display_info.tvmode.stereo_type = GDL_STEREO_NONE;
      display_info.flags = 0;
      display_info.bg_color = 0;
      display_info.gamma = GDL_GAMMA_LINEAR;
      display_info.color_space = GDL_COLOR_SPACE_BT709;

      gdl_result = gdl_set_display_info(&display_info);
      OS_ASSERT((gdl_result == GDL_SUCCESS) && "Set display info");
   }
   OS_ASSERT((gdl_result == GDL_SUCCESS) && "Get display info");

   return success;
}



static void add_hdmi_audio(void) {
   ismd_result_t smd_result = ISMD_SUCCESS;

   if ((output_handle_hdmi == AUDIO_INVALID_HANDLE) && enable_edid()) {
      smd_result = ismd_audio_output_get_handle_by_hw_id(global_processor_h, GEN3_HW_OUTPUT_HDMI, &output_handle_hdmi);
      if (smd_result != ISMD_SUCCESS) {
         smd_result = ismd_audio_add_phys_output(global_processor_h, GEN3_HW_OUTPUT_HDMI, audio_out_config, &output_handle_hdmi);
         OS_ASSERT((smd_result == ISMD_SUCCESS) && "Create HDMI output");
      }
      smd_result = ismd_audio_output_enable(global_processor_h, output_handle_hdmi);
      OS_ASSERT((smd_result == ISMD_SUCCESS) && "Enable HDMI output");
   }
}



static void remove_hdmi_audio(void) {
   ismd_result_t smd_result = ISMD_SUCCESS;

   if (output_handle_hdmi != AUDIO_INVALID_HANDLE) {
      smd_result = ismd_audio_remove_output(global_processor_h, output_handle_hdmi);
      OS_ASSERT((smd_result == ISMD_SUCCESS) && "Remove HDMI output");
      output_handle_hdmi = AUDIO_INVALID_HANDLE;
   }
}

// Hotplug event handler
static void gdl_event_callback(gdl_app_event_t event, void *user_data)
{
   (void)user_data;

   switch(event){
      case GDL_APP_EVENT_HOTPLUG_DETECT:
         add_hdmi_audio();
         break;
      case GDL_APP_EVENT_HOTPLUG_LOSS:
         remove_hdmi_audio();
         break;
      default:
         break;
   }
}




//****************************************************************************
// NAME:
//      configure_audio_master_clock
//
// SYNOPSIS:
//      configure_audio_master_clock(ismd_audio_processor_t processor_h, unsigned int freq_fam)
//
// RETURNS:
//      ISMD_SUCCESS on success.
//
// DESCRIPTION:
//     This function sets up the audio master clock frequency
//
//****************************************************************************
static ismd_result_t
configure_audio_master_clock(ismd_audio_processor_t  proc_h, unsigned int freq)
{
   soc_user_info_t info;
   ismd_result_t result = ISMD_ERROR_OPERATION_FAILED;

   if ((result = system_utils_get_soc_info(&info)) == 0){

      if((info.name_enum == SOC_NAME_CE3100) || (info.name_enum == SOC_NAME_CE4200)){
         if((result = ismd_audio_configure_master_clock(proc_h, freq, ISMD_AUDIO_CLK_SRC_INTERNAL)) != ISMD_SUCCESS) {
            OS_PRINT("ismd_audio_configure_master_clock failed!\n", result);
         }
      }
      else if(info.name_enum == SOC_NAME_CE4100){
         if((result = ismd_audio_configure_master_clock(proc_h, freq, ISMD_AUDIO_CLK_SRC_EXTERNAL)) != ISMD_SUCCESS) {
            OS_PRINT("ismd_audio_configure_master_clock failed!\n", result);
         }
      }
      else
      {
         OS_INFO("system_utils_get_soc_info returned unrecognized chip info! Defaulting to external master audio clock source\n");
         if((result = ismd_audio_configure_master_clock(proc_h, freq, ISMD_AUDIO_CLK_SRC_INTERNAL)) != ISMD_SUCCESS) {
            OS_PRINT("ismd_audio_configure_master_clock failed!\n", result);
         }
      }
   }
   else {
      OS_PRINT("Could not read chip information for audio master clock configuration!\n", result);
   }

   return result;
}

#define PERIOD	1024

ismd_result_t smd_write(int fd)
{
   ismd_result_t result = ISMD_SUCCESS;
   ismd_port_status_t port_status;
   ismd_buffer_handle_t buffer = ISMD_BUFFER_HANDLE_INVALID;
   ismd_buffer_descriptor_t buffer_desc;
   int times_buffer_alloc_failed = 0;
   unsigned int amount_to_read;
   unsigned int amount_read = 0;
   void *virtual_buffer_address = NULL;
   bool full = false;


						printf("ismd buffer allocation\n");
                        result = ismd_buffer_alloc(32*1024, &buffer);
                        if( ISMD_SUCCESS != result )
							printf("ismd buffer allocation fail\n");
						printf("ismd buffer allocation finished\n");

                        result = ismd_buffer_read_desc(buffer, &buffer_desc);
                        if( ISMD_SUCCESS == result )
                        {
                            buffer_desc.phys.level = 32*1024;
                            // Now commit the changes
                            result = ismd_buffer_update_desc(buffer, &buffer_desc );
							printf("ismd read okay\n");
                        } else {
							printf("ismd read fail\n");
							return result;
						}
                buffer_desc.phys.level = 0;
                buffer_desc.phys.size = 32*1024; 
                // Do this as a separate if statement to reduce the nesting...

                // do the actual write into the buffer
                if( result == ISMD_SUCCESS )
                {
                    virtual_buffer_address = OS_MAP_IO_TO_MEM_CACHE(buffer_desc.phys.base, buffer_desc.phys.size);
                    OS_ASSERT(virtual_buffer_address);
					printf("MMAP finished!\n");

                    amount_read = read(fd, virtual_buffer_address, PERIOD);
                    if( amount_read != PERIOD )
                    {
						printf("read fail...%d\n", amount_read);
                        result = ISMD_ERROR_OPERATION_FAILED;
                    }


                    OS_UNMAP_IO_FROM_MEM(virtual_buffer_address, buffer_desc.phys.size);
                }

#if 0
                // update the descriptor
                if( result == ISMD_SUCCESS )
                {
                    buffer_desc.phys.level = amount_read;
                    ((ismd_es_buf_attr_t *)buffer_desc.attributes)->local_pts = ISMD_NO_PTS;


                    result = ismd_buffer_update_desc(buffer, &buffer_desc);
                    if( result != ISMD_SUCCESS )
                    {
                    }
                }

#endif
                // write buffer to the port/queue
                if( result == ISMD_SUCCESS )
                {
                    result = ismd_port_write(port_h, buffer);
                    if( result != ISMD_SUCCESS )
                    {
                    }
					else {
						printf("port write finished!\n");
					}
                }

                if( result == ISMD_SUCCESS )
                {
                }

   return result;
}

// entry point - set things up
int main (int argc, char **argv) {
   ismd_result_t        smd_result = ISMD_SUCCESS;
   gdl_ret_t            gdl_result = GDL_SUCCESS;
   bool                 added_i2s0 = false, added_i2s1 = false, added_spdif = false;
   int fd, size;
   struct stat st;
   int ret = 0;
   ismd_event_t port_event;

   if (argc < 2) {
      OS_PRINT("Usage: %s <mp3 filename>\n", argv[0]);
      exit(1);
   }


   fd = open(argv[1], O_RDWR);
   if (fd < 0) {
	   printf("open file error\n");
	   close(fd);
   }

   printf("#######New Test########\n");

   ret = fstat(fd, &st);
   if (ret < 0) {
	   printf("get file stat\n");
	   close(fd);
   }
   printf("file size %d\n", st.st_size);

   printf("readout wav header!\n");

   //*******************************************************************
   // Init filesource and GDL
   //*******************************************************************
  // pl_filesource_init();
   gdl_result = gdl_init(0);
   OS_ASSERT((gdl_result == GDL_SUCCESS) && "Initialize GDL");

   printf("#######gdl########\n");
   //*******************************************************************
   // Register display callbacks for HDMI "hotplug"
   //*******************************************************************
   gdl_result = gdl_event_register(GDL_APP_EVENT_HOTPLUG_DETECT, gdl_event_callback, NULL);
   OS_ASSERT((gdl_result == GDL_SUCCESS) && "Register hotplug callback");

   gdl_result = gdl_event_register(GDL_APP_EVENT_HOTPLUG_LOSS, gdl_event_callback, NULL);
   OS_ASSERT((gdl_result == GDL_SUCCESS) && "Register hotplug loss callback");

   //*******************************************************************
   // Now setup the audio driver
   //*******************************************************************
   smd_result = ismd_audio_open_global_processor(&global_processor_h);
   OS_ASSERT((smd_result == ISMD_SUCCESS) && "Open global audio processor");

   smd_result = ismd_audio_add_input_port(global_processor_h, false, &input_h, &port_h);
   OS_ASSERT((smd_result == ISMD_SUCCESS) && "Add audio input port");

   smd_result = ismd_audio_input_set_data_format(global_processor_h, input_h, ISMD_AUDIO_MEDIA_FMT_PCM);
   OS_ASSERT((smd_result == ISMD_SUCCESS) && "Set PCM format");

   printf("#######format########\n");
   smd_result = ismd_audio_input_get_notification_event(global_processor_h, input_h, ISMD_AUDIO_NOTIFY_STREAM_END, &eos_event_h);
   OS_ASSERT((smd_result == ISMD_SUCCESS) && "Get EOS event");

   // add outputs
   // Only do this if they aren't already added
   smd_result = ismd_audio_output_get_handle_by_hw_id(global_processor_h, GEN3_HW_OUTPUT_I2S0, &output_handle_i2s0);
   if (smd_result != ISMD_SUCCESS) {
      smd_result = ismd_audio_add_phys_output(global_processor_h, GEN3_HW_OUTPUT_I2S0, audio_out_config, &output_handle_i2s0);
      OS_ASSERT((smd_result == ISMD_SUCCESS) && "Create I2S1 output");
      added_i2s0 = true;
   }
   smd_result = ismd_audio_output_get_handle_by_hw_id(global_processor_h, GEN3_HW_OUTPUT_I2S1, &output_handle_i2s1);
   if (smd_result != ISMD_SUCCESS) {
      smd_result = ismd_audio_add_phys_output(global_processor_h, GEN3_HW_OUTPUT_I2S1, audio_out_config, &output_handle_i2s1);
      OS_ASSERT((smd_result == ISMD_SUCCESS) && "Create I2S1 output");
      added_i2s1 = true;
   }
   smd_result = ismd_audio_output_get_handle_by_hw_id(global_processor_h, GEN3_HW_OUTPUT_SPDIF, &output_handle_spdif);
   if (smd_result != ISMD_SUCCESS) {
      smd_result = ismd_audio_add_phys_output(global_processor_h, GEN3_HW_OUTPUT_SPDIF, audio_out_config, &output_handle_spdif);
      OS_ASSERT((smd_result == ISMD_SUCCESS) && "Create SPDIF output");
      added_spdif = true;
   }
   if (enable_edid()) {
      add_hdmi_audio();
   }

   // Set PLL and clock dividers
   if (added_i2s0 && added_i2s1 && added_spdif) {
      configure_audio_master_clock(global_processor_h, 36864000);  // only needs to be done if there weren't any outputs already
   }

   // enable outputs
   smd_result = ismd_audio_output_enable(global_processor_h, output_handle_i2s0);
   OS_ASSERT((smd_result == ISMD_SUCCESS) && "Enable I2S0 output");
   smd_result = ismd_audio_output_enable(global_processor_h, output_handle_i2s1);
   OS_ASSERT((smd_result == ISMD_SUCCESS) && "Enable I2S1 output");
   smd_result = ismd_audio_output_enable(global_processor_h, output_handle_spdif);
   OS_ASSERT((smd_result == ISMD_SUCCESS) && "Enable SPDIF output");
   if (output_handle_hdmi != AUDIO_INVALID_HANDLE) {
      smd_result = ismd_audio_output_enable(global_processor_h, output_handle_hdmi);
      OS_ASSERT((smd_result == ISMD_SUCCESS) && "Enable HDMI output");
   }

#if 1 
   smd_result = ismd_audio_input_set_pcm_format(global_processor_h, input_h, 16, 48000, 0xfffffff0);
#endif
	
if (smd_result != ISMD_SUCCESS)
	printf("set pcm formats fail\n");
else	
	printf("set pcm formats successfully\n");

   //*******************************************************************
   // Start the filesource and audio
   //*******************************************************************
#if 0
   smd_result = pl_filesource_open(&filesource);
   OS_ASSERT((smd_result == ISMD_SUCCESS) && "Open filesource");

   smd_result = pl_filesource_set_port(filesource, port_h);
   OS_ASSERT((smd_result == ISMD_SUCCESS) && "Set filesource port");

   smd_result = pl_filesource_set_path(filesource, argv[1], strlen(argv[1])+1);
   OS_ASSERT((smd_result == ISMD_SUCCESS) && "Set filesource path");

   smd_result = pl_filesource_enable_eos(filesource, true);
   OS_ASSERT((smd_result == ISMD_SUCCESS) && "Enable EOS in filesource");

   smd_result = pl_filesource_set_state(filesource, ISMD_DEV_STATE_PLAY);
   OS_ASSERT((smd_result == ISMD_SUCCESS) && "Start filesource");

#endif
   smd_result = ismd_event_alloc(&port_event);
   if (smd_result != ISMD_SUCCESS) {
	   printf("ismd event allocation\n");
   }

   smd_result = ismd_port_attach(port_h, port_event, ISMD_QUEUE_EVENT_NOT_FULL|ISMD_QUEUE_EVENT_EMPTY, ISMD_QUEUE_WATERMARK_NONE);
   if (smd_result != ISMD_SUCCESS) {
	   printf("ismd port attach fail\n");
   }

   printf("ismd port attach okay\n");
   smd_result = smd_write(fd);
   if (smd_result != ISMD_SUCCESS) {
   		printf("ismd write fail\n");
	   goto finish;
   }
   else 
   		printf("ismd write okay\n");
   // start audio
   smd_result = ismd_dev_set_state(input_h, ISMD_DEV_STATE_PLAY);
   OS_ASSERT((smd_result == ISMD_SUCCESS) && "Start audio");

   // wait for the EOS event from audio
   smd_result = ismd_event_wait(eos_event_h, ISMD_TIMEOUT_NONE);
   OS_ASSERT((smd_result == ISMD_SUCCESS) && "Wait for EOS event");

finish:
   gdl_close();
   exit(0);
   return smd_result;
}
